package cn.flying.cloud.oauth.web.oauth;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.ParseException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTParser;
import com.nimbusds.jwt.SignedJWT;

import cn.flying.cloud.base.common.bo.UserInfo;
import cn.flying.cloud.base.common.constants.Constants;
import cn.flying.cloud.base.common.utils.Ft;
import cn.flying.cloud.base.common.utils.lang.CaptchaUtil;
import cn.flying.cloud.base.common.vo.Rt;
import cn.flying.cloud.base.redis.service.RedisService;
import cn.flying.cloud.message.feign.MessageApiClient;
import cn.flying.cloud.oauth.entity.OauthUserDetail;
import cn.flying.cloud.oauth.server.configuration.constant.Constant;
import cn.flying.cloud.oauth.server.configuration.details.UserDetail;
import cn.flying.cloud.oauth.service.OauthUserDetailService;
import cn.flying.cloud.system.feign.UserInfoApiClient;

/**
 * oauth登录注册处理器
 *
 * @author: admin
 * @date: 2023年04月17日 13:21
 * @version: 1.0
 */
@Controller
@RequestMapping("/oauth")
public class OauthLoginController {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    private OauthUserDetailService oauthUserDetailService;
    @Resource
    private UserInfoApiClient userInfoApiClient;
    @Resource
    private RedisService redisService;
    @Resource
    private TokenStore tokenStore;

    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    private MessageApiClient messageApiClient;

    /**
     * 跳转到登录页面
     *
     * @return
     */
    @RequestMapping("/login/form")
    public String loginForm(HttpServletRequest request, HttpServletResponse response) {
        return "oauth-login";
    }

    /**
     * 跳转到注册页面
     *
     * @return
     */
    @RequestMapping("/register/form")
    public String registerPage(HttpServletRequest request, Model model) {
        if (Ft.String.isNotBlank(request.getParameter("return_to"))) {
            // 回调地址
            try {
                String return_to = URLEncoder.encode(request.getParameter("return_to"), "UTF-8");
                model.addAttribute("return_to", return_to);
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
        }
        return "oauth-register";
    }

    /**
     * 获取图片验证码
     *
     * @param request
     * @param response
     * @throws IOException
     */
    @RequestMapping("/user/captcha")
    public ResponseEntity<byte[]> captcha(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String uid = request.getParameter("uid");
        /*
             1.生成验证码
             2.把验证码上的文本存在缓存中
             3.把验证码图片发送给客户端
             */
        CaptchaUtil captchaUtil = new CaptchaUtil(120, 38);
        BufferedImage image = captchaUtil.getImage();
        logger.info("生成的验证码：{}, uid：{}", captchaUtil.getText(), uid);
        //存入Redis
        redisService.set(Constants.AUTH_CODE + ":captcha:" + uid, captchaUtil.getText(), 3 * 60 * 1000L);

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ImageIO.write(image, "png", outputStream);
        byte[] bytes = outputStream.toByteArray();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.IMAGE_PNG);
        return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
    }
//    public void captcha(HttpServletRequest request, HttpServletResponse response) throws IOException {
//        /*
//             1.生成验证码
//             2.把验证码上的文本存在session中
//             3.把验证码图片发送给客户端
//             */
//        CaptchaUtil captchaUtil = new CaptchaUtil(120, 38);
//        BufferedImage image = captchaUtil.getImage();
//        request.getSession().setAttribute(Constants.SESSION_CAPTCHA_CODE, captchaUtil.getText());
//        captchaUtil.output(image, response.getOutputStream());
//    }

    /**
     * 用户发送验证码
     *
     * @return
     */
    @RequestMapping("/user/send/code")
    @ResponseBody
    public Rt<?> sendCode(HttpServletRequest request) {
        String username = request.getParameter("username");
        if (Pattern.compile(Constant.MOBILE_REGEX).matcher(username).matches()) {
            // 手机验证码
            // TODO 实际发生短信验证码
            String code = "1234";
            String mobile = "1234";

            redisService.set(Constants.AUTH_CODE + ":sms:code:" + username, code, 5 * 60 * 1000L);
            logger.info("发送的短信验证码：{}, mobile：{}", code, mobile);
        } else if (Pattern.compile(Constant.EMAIL_REGEX).matcher(username).matches()) {
            // 发送邮件验证码
            // 生成验证码
            CaptchaUtil captchaUtil = new CaptchaUtil();
            String code = captchaUtil.getCode();
            Map<String, Object> data = new HashMap<>();
            data.put("verifyCode", Arrays.asList(code.split("")));

            // TODO
//            messageServiceApi.sendHtmlMail(Arrays.asList(username), "验证码", "verify-code", data, MsgTypeEnum.M_101);
            redisService.set(Constants.AUTH_CODE + ":email:code:" + username, code, 5 * 60 * 1000L);
            logger.info("发送的邮箱验证码：{}, email：{}", code, "");
        } else {
            return Rt.error("发送失败，账号不能识别！");
        }
        return Rt.success("发送成功！");
    }

    /**
     * 用户注册提交
     *
     * @return
     */
    @RequestMapping("/register")
    @ResponseBody
    public Rt<?> register(HttpServletRequest request) {
        String username = request.getParameter("username");
        String name = request.getParameter("name");
        String password = request.getParameter("password");
        String email = request.getParameter("email");
        String mobile = request.getParameter("mobile");
        String code = request.getParameter("code");

        String cacheCode = Constants.AUTH_CODE + ":email:code:" + email;
        if (StringUtils.isBlank(cacheCode) || !cacheCode.equalsIgnoreCase(code)) {
            return Rt.error("注册失败，验证码不正确！");
        }
        UserInfo info = new UserInfo();
        info.setUserId("");
        info.setUsername(username);
        info.setPassword(passwordEncoder.encode(password));
        info.setName(name);
//        info.setGender("");
        info.setBirthday(LocalDate.now());
        info.setMobile(mobile);
        info.setEmail(email);
//        info.setAddress("");
//        info.setAvatar("");
//        info.setOrgId("");
//        info.setOrgName("");
        info.setStatus("Y");
        info.setLocked("N");
//        info.setRemark("");
        info.setTenantId("");

        String userId = userInfoApiClient.register(info);
        OauthUserDetail detail = new OauthUserDetail();
        detail.setUserId(userId);
        detail.setUsername(username);
        detail.setPassword(password);
        detail.setName(name);
//        detail.setOrgId("");
//        detail.setOrgName("");
//        detail.setGender("");
//        detail.setAge(0);
//        detail.setBirthday(LocalDate.now());
        detail.setEmail(email);
        detail.setMobile(mobile);
//        detail.setAvatar("");
        detail.setLocked("N");
        detail.setStatus("Y");
        detail.setTenantId("");

        oauthUserDetailService.save(detail);
        // TODO
        return Rt.success("注册成功！");
    }

    /**
     * 跳转到重置密码页面
     *
     * @return
     */
    @RequestMapping("/forget/form")
    public String forgetPage(HttpServletRequest request, Model model) {
        if (Ft.String.isNotBlank(request.getParameter("return_to"))) {
            // 回调地址
            try {
                String return_to = URLEncoder.encode(request.getParameter("return_to"), "UTF-8");
                model.addAttribute("return_to", return_to);
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
        }
        return "oauth-forget";
    }

    /**
     * 用户充值密码提交
     *
     * @return
     */
    @RequestMapping("/reset")
    @ResponseBody
    public Rt<?> reset(HttpServletRequest request) {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String mobile = request.getParameter("mobile");
        String code = request.getParameter("code");

        String cacheCode = Constants.AUTH_CODE + ":sms:code:" + mobile;
        if (StringUtils.isBlank(cacheCode) || !cacheCode.equalsIgnoreCase(code)) {
            return Rt.error("重置密码失败，验证码不正确！");
        }
        return Rt.success();
    }

    /**
     * 用户退出登录（oauth2认证方式）
     *
     * @return
     */
    @RequestMapping("/logout")
    @ResponseBody
    public Rt<?> logout(HttpServletRequest request) {
        Rt<?> rt = Rt.success();
        String token = Ft.Servlet.getToken(request);
        if (Ft.String.isBlank(token)) {
            rt = Rt.error();
            logger.error("用户退出登录失败，Token为空");
            return rt;
        }
        String accessToken = token.replace("Bearer ", "");

        OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(accessToken);
        if (oAuth2AccessToken != null) {
            // 移除access_token
            tokenStore.removeAccessToken(oAuth2AccessToken);
            if (oAuth2AccessToken.getRefreshToken() != null) {
                // 移除refresh_token
                tokenStore.removeRefreshToken(oAuth2AccessToken.getRefreshToken());
            }
            logger.info("用户退出登录，并移除Token");
        }

        // 删除redis
        try {
            JWT jwt = JWTParser.parse(accessToken);
            Map<String, Object> object = ((SignedJWT) jwt).getPayload().toJSONObject();
            String jti = (String) object.get("jti");
            String tokenKey = Constants.ACCESS_TOKEN + ":" + jti;
            redisService.del(tokenKey);
        } catch (ParseException e) {
            rt = Rt.error();
            logger.error("用户退出登录，解析Token失败", e);
        }

        return rt;
    }

    /**
     * 获取用户信息
     *
     * @return
     */
    @GetMapping("getUserInfo")
    public Rt<?> getUserInfo() {
        String username = (String) Ft.ThreadLocal.get(Constants.USER_NAME);
        UserInfo authInfo = userInfoApiClient.getUserAuthInfo(username, "username");
        //不要返回密码信息
        authInfo.setPassword(null);

        List<String> authorities = new ArrayList<>();
        //用户的角色
        if (CollectionUtils.isEmpty(authInfo.getRoles())) {
            //处理角色，增加前缀“ROLE_”
            List<String> roles = authInfo.getRoles().stream().map(roleId -> "ROLE_" + roleId).distinct().collect(Collectors.toList());
            authorities.addAll(roles);
        }

        //用户所有的权限资源
        if (CollectionUtils.isNotEmpty(authInfo.getPerms())) {
            authorities.addAll(authInfo.getPerms());
        }

        //将用户的角色、资源、权限保存到Redis缓存中
        redisService.set(Constants.AUTH_PERMS + ":" + authInfo.getTenantId() + ":" + authInfo.getUserId(), Ft.JSON.serializer(authorities));

        UserDetail userDetail = new UserDetail();
        BeanUtils.copyProperties(authInfo, userDetail);

        // 获取当前的Authentication对象
        Authentication currentAuth = SecurityContextHolder.getContext().getAuthentication();
        // 创建一个新的AuthenticationToken，使用更新后的UserDetails
        UsernamePasswordAuthenticationToken newAuth = new UsernamePasswordAuthenticationToken(userDetail, currentAuth.getCredentials(), userDetail.getAuthorities());
        // 设置细节，如已认证的凭据、详情等
        newAuth.setDetails(currentAuth.getDetails());
        // 如果认证成功，手动设置到SecurityContextHolder
        if (newAuth.isAuthenticated()) {
            SecurityContextHolder.getContext().setAuthentication(newAuth);
        }

        return Rt.success(authInfo);
    }

}
